import pandas as pd
import akshare as ak
from datetime import datetime, timedelta
import time
import random
import logging

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 修改为场内指数代码 512480
fund_code = "sh512480"
print(f"开始获取ETF代码 {fund_code} 的数据...")

# 增强的数据获取函数
def get_etf_data(fund_code):
    """
    尝试多种方法获取ETF数据，添加更健壮的错误处理和重试机制
    """
    # 尝试不同的代码格式
    code_formats = [fund_code, f"sh{fund_code}", f"sz{fund_code}"]
    
    # 方法配置 - 包含方法名称、函数和适用的代码格式
    data_methods = [
        # 方法1: 东方财富ETF历史数据
        # {
        #     "name": "东方财富ETF数据源", 
        #     "func": ak.fund_etf_hist_em,
        #     "param_name": "symbol",
        #     "timeout": 30
        # },
        # 方法2: 新浪ETF历史数据
        {
            "name": "新浪ETF数据源", 
            "func": ak.fund_etf_hist_sina,
            "param_name": "symbol",
            "timeout": 20
        },
        # 方法3: 尝试作为A股获取
        {
            "name": "A股历史数据", 
            "func": ak.stock_zh_a_hist,
            "param_name": "symbol",
            "extra_params": {"period": "daily", "adjust": "qfq"},
            "timeout": 20
        },
        # 方法4: 尝试作为指数获取
        {
            "name": "指数接口", 
            "func": ak.stock_zh_index_daily,
            "param_name": "symbol",
            "timeout": 20
        }
    ]
    
    max_retries = 3  # 每个方法的最大重试次数
    
    # 遍历所有代码格式和数据源组合
    for code in code_formats:
        print(f"\n🔄 尝试代码格式: {code}")
        
        for method_idx, method in enumerate(data_methods, 1):
            for retry in range(max_retries):
                try:
                    print(f"\n方法{method_idx}: 尝试使用{method['name']}...")
                    
                    # 准备参数
                    kwargs = {method['param_name']: code}
                    if 'extra_params' in method:
                        kwargs.update(method['extra_params'])
                    
                    # 设置超时
                    if 'timeout' in method:
                        import socket
                        socket.setdefaulttimeout(method['timeout'])
                    
                    # 执行函数获取数据
                    df = method['func'](**kwargs)
                    
                    # 检查返回数据是否有效
                    if df is not None and not df.empty:
                        print(f"✅ 成功从{method['name']}获取数据")
                        print(f"数据维度: {df.shape[0]}行 × {df.shape[1]}列")
                        print(f"列名: {list(df.columns)}")
                        return df
                    else:
                        print(f"⚠️ {method['name']}返回的数据为空")
                except Exception as e:
                    error_msg = str(e)
                    # 避免显示过长的错误信息
                    if len(error_msg) > 100:
                        error_msg = error_msg[:100] + "..."
                    print(f"❌ {method['name']}错误: {error_msg}")
                    logging.error(f"Method {method_idx} with code {code} failed: {error_msg}")
                    
                    # 如果是最后一次重试，则不等待
                    if retry < max_retries - 1:
                        wait_time = random.uniform(2, 5)  # 增加等待时间
                        print(f"⏱️ 将在{wait_time:.2f}秒后重试 ({retry + 2}/{max_retries})")
                        time.sleep(wait_time)
                    else:
                        print(f"❌ {method['name']}已达到最大重试次数，尝试下一个数据源")
    
    return None

# 尝试获取数据
df = get_etf_data(fund_code)
# 如果成功获取到数据，则进行处理
if df is not None:
    print("\n📊 开始处理获取到的数据...")
    
    # 根据不同数据源的列名进行重命名
    if 'date' in df.columns:
        df = df.rename(columns={
            'date': '日期',
            'open': '开盘',
            'close': '收盘',
            'high': '最高',
            'low': '最低',
            'volume': '成交量'
        })
    elif '日期' not in df.columns and '交易日期' in df.columns:
        df = df.rename(columns={'交易日期': '日期'})
    
    # 确保日期列是datetime类型
    if '日期' in df.columns:
        df['日期'] = pd.to_datetime(df['日期'])
        print(f"日期列类型: {df['日期'].dtype}")
        
        # 筛选2025年的数据
        print("\n🔍 筛选2025年的数据...")
        df_2025 = df[df['日期'].dt.year == 2025].copy()
        print(f"2025年数据总行数: {len(df_2025)}")
        
        if not df_2025.empty:
            print(f"2025年日期范围: {df_2025['日期'].min()} 到 {df_2025['日期'].max()}")
            
            # 打印2025年数据预览
            print("\n2025年数据预览:")
            print(df_2025.head())
            
            # 显示2025年数据统计信息
            print("\n2025年数据统计信息:")
            print(f"开盘价统计: 最低={df_2025['开盘'].min():.4f}, 最高={df_2025['开盘'].max():.4f}, 平均={df_2025['开盘'].mean():.4f}")
            print(f"收盘价统计: 最低={df_2025['收盘'].min():.4f}, 最高={df_2025['收盘'].max():.4f}, 平均={df_2025['收盘'].mean():.4f}")
            
            # 保存2025年数据到CSV
            csv_file_2025 = f"{fund_code}_2025_data.csv"
            df_2025.to_csv(csv_file_2025, index=False, encoding='utf-8-sig')
            print(f"\n✅ 2025年数据已保存到: {csv_file_2025}")
            
            # 覆盖原始df变量，只保留2025年数据
            df = df_2025
        else:
            print("❌ 未找到2025年的数据")
    
    # 显示最终处理后的数据统计信息
    if not df.empty:
        print("\n📋 最终数据统计信息:")
        print(f"数据总行数: {len(df)}")
        print(f"日期范围: {df['日期'].min()} 到 {df['日期'].max()}")
        
        # 计算KDJ指标
        print("\n📊 计算KDJ指标...")
        n = 9  # 周期
        df['最高价'] = df['最高']
        df['最低价'] = df['最低']
        
        # 计算RSV值
        df['RSV'] = (df['收盘'] - df['最低价'].rolling(n).min()) / \
                    (df['最高价'].rolling(n).max() - df['最低价'].rolling(n).min()) * 100
        
        # 计算K值和D值
        df['KDJ_K'] = df['RSV'].ewm(com=2, adjust=False).mean()
        df['KDJ_D'] = df['KDJ_K'].ewm(com=2, adjust=False).mean()
        
        # 计算J值
        df['KDJ_J'] = 3 * df['KDJ_K'] - 2 * df['KDJ_D']
        
        # 筛选最近1年且KDJ中J<0的数据
        print("\n🔍 筛选最近1年KDJ中J<0的数据...")
        one_year_ago = df['日期'].max() - pd.DateOffset(years=1)
        recent_df = df[df['日期'] >= one_year_ago].copy()
        
        # 筛选J<0的数据
        j_less_than_0 = recent_df[recent_df['KDJ_J'] < 10].copy()
        
        if not j_less_than_0.empty:
            print(f"找到 {len(j_less_than_0)} 个满足条件的日期")
            print("\n📈 J<0 的日期和股票价格:")
            # 按日期排序
            j_less_than_0 = j_less_than_0.sort_values('日期')
            # 输出结果
            result_df = j_less_than_0[['日期', '收盘', 'KDJ_K', 'KDJ_D', 'KDJ_J']]
            print(result_df.to_string(index=False, formatters={
                '日期': lambda x: x.strftime('%Y-%m-%d'),
                '收盘': '{:.2f}'.format,
                'KDJ_K': '{:.2f}'.format,
                'KDJ_D': '{:.2f}'.format,
                'KDJ_J': '{:.2f}'.format
            }))
            
            # 保存结果到CSV
            csv_file_kdj = f"{fund_code}_kdj_j_less_than_0.csv"
            result_df.to_csv(csv_file_kdj, index=False, encoding='utf-8-sig', date_format='%Y-%m-%d')
            print(f"\n✅ 结果已保存到: {csv_file_kdj}")
        else:
            print("❌ 最近1年内未找到KDJ中J<0的数据")
            
    else:
        print("\n⚠️ 最终数据为空")
    
else:
    print("\n❌ 所有尝试的数据源都无法获取该ETF的数据")
    print("建议: 检查ETF代码是否正确，或尝试更新akshare库到最新版本")
    # 提示可能的正确代码格式
    print("\n常见的ETF代码格式:")
    print("1. 直接使用6位数字代码: 如 '512480'")
    print("2. 带市场标识: 如 'sh512480' 或 'sz512480'")
    print("3. 检查是否需要使用基金代码而非ETF代码")